package com.dai.nettyserve.netty.serve;

import com.dai.nettyserve.netty.bean.ChargeCmd;
import com.dai.nettyserve.netty.manager.NettyClientStore;
import com.dai.nettyserve.netty.manager.SessionManager;
import com.dai.nettyserve.utils.HexAsciiUtil;
import com.dai.nettyserve.utils.NumericalUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * @Author: 代
 * @Description:netty消息处理
 * @Date: Created in 11:51 2019/3/23
 */
@Slf4j
@Scope("prototype")//配置成多例模式
@Component
@ChannelHandler.Sharable
public class DataServerInHandler extends ChannelInboundHandlerAdapter {
    Logger logger = LoggerFactory.getLogger(DataServerInHandler.class);

    /*@Resource
    private ISystemHardwareIpRecordService systemHardwareIpRecordService;*/

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        logger.info("设备创建连接！handlerAdded");
        super.handlerAdded(ctx);
    }


    //设备离线
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved");
        logger.info("设备连接断开！handlerRemoved");

        //移除  SessionManager绑定的该设备链接通道
        SessionManager.removeSession(SessionManager.getGprsNo(ctx), ctx);
        // netty客户端    封装给硬件发送消息的工具
        NettyClientStore.clientMap.remove(SessionManager.getGprsNo(ctx), ctx);
        //  通过SN 更改库存网络开关设备状态为 离线状态
//        log.info("后台查询的设备Sn={}", SessionManager.getGprsNo(ctx));
       /* SystemHardwareIpRecord systemHardwareIpRecord = systemHardwareIpRecordService.selectSystemHardwareIpRecordBySn(SessionManager.getGprsNo(ctx));
        if (null != systemHardwareIpRecord) {
            //0拍照相机1中控器2LED屏
            if ("1".equals(systemHardwareIpRecord.getHardwareType())) {
                //设备状态(0在线1离线)
                systemHardwareIpRecord.setDeviceState("1");
                int x = systemHardwareIpRecordService.updateSystemHardwareIpRecord(systemHardwareIpRecord);
                if (x > 0) {
                    log.info("--------网络开关设备离线成功---sn={}-----", systemHardwareIpRecord.getDeviceSn());
                } else {
                    log.error("-------网络开关设备离线成功,但更改库存设备状态失败---sn={}-----", systemHardwareIpRecord.getDeviceSn());
                }
            } else {
                log.error("根据SN在后台能找到硬件设备,但设备类型配置的不对,该SN应属于网络开关");
            }
        } else {
            log.error("根据网络开关的SN离线设备,但后台通过SN找不到网络开关设备");
        }*/
        ctx.close();
    }

    @Override
    public boolean isSharable() {
//        System.out.println("isSharable");
        log.info("isSharable");
        return super.isSharable();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        System.out.println("channelActive");
        logger.info("设备消息链路激活！channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelInactive(ctx);
    }

    //注意   绑定的deviceSn是16进制数据
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        logger.info("-----------------receive msg is " + msg + "-----------------");
        String receMsg = (String) msg;
        receMsg = receMsg.substring(0, 7) + "CM" + receMsg.substring(7);
       ctx.writeAndFlush(hexStringToBytes("A60007"  + "EE01"));    //测试响应数据
//        logger.info("yao处理的数据===={}", receMsg);
        //处理心跳回应
//        if (receMsg.toLowerCase().contains("a50012594a") || receMsg.toLowerCase().contains("a60005ffaa")) {
        if (receMsg.toLowerCase().contains("{a50012cm594a")) {
            //{a50012CM594a30303930433243413230454205}
            //A5 00 2F 50 F6 42 30 33 35 39 46 32 30 39 30 39 30 31 31 31 31 31 31 31 31 31 31 31 31 DC CD 01 AA AA A1 00 11 E1 01 55 00 02 00 02 CD DC 87
//            ctx.writeAndFlush(hexStringToBytes("A5 00 2F 50 F6 42 30 33 35 39 46 32 30 39 30 39 30 31 31 31 31 31 31 31 31 31 31 31 31 DC CD 01 AA AA A1 00 11 E1 01 55 00 02 00 02 CD DC 87"));
//            String commod = receMsg.substring(7, 11);
            String newCommod = receMsg.substring(7, 13);
            String gprsNo = receMsg.substring(13, 37);
//            logger.info("newCommod==={}", newCommod);
//            logger.info("心跳命令制=={}", commod);
            //查看存的是否有该设备的channel
            String bindGprsNo = SessionManager.getGprsNo(ctx);
            //优雅判断非空
            if (null == bindGprsNo) {
                //上线前， 所留的SessionManager
//                logger.info("该设备上线前的SessionManager===={}", SessionManager.getSessions());
                //gprsNo    十六进制SN-->10进制-->ASCII
                String deviceNo = HexAsciiUtil.hex2Str(gprsNo);

                log.info("设备16进制序列号=={}", gprsNo);
                log.info("设备序列号=={}", deviceNo);
                //设备 重新上线
                SessionManager.putSession(deviceNo, ctx);
                NettyClientStore.clientMap.put(deviceNo, ctx);
                logger.info("clientMap==== {}", NettyClientStore.clientMap);
                //更新库中设备在线状态
              /*  logger.info("网络开关设备重新上线,准备更新库中设备状态");
                SystemHardwareIpRecord systemHardwareIpRecord = systemHardwareIpRecordService.selectSystemHardwareIpRecordBySn(deviceNo);
                if (null != systemHardwareIpRecord) {
                    if ("1".equals(systemHardwareIpRecord.getHardwareType())) {
                        //设备状态(0在线1离线)
                        systemHardwareIpRecord.setDeviceState("0");
                        int x = systemHardwareIpRecordService.updateSystemHardwareIpRecord(systemHardwareIpRecord);
                        if (x > 0) {
                            log.info("--------网络开关设备上线成功---sn={}-----", systemHardwareIpRecord.getDeviceSn());
                        } else {
                            log.error("-------网络开关设备上线成功,但更改库存设备状态失败---sn={}-----", systemHardwareIpRecord.getDeviceSn());
                        }
                    } else {
                        log.error("根据SN在后台能找到硬件设备,但设备类型配置的不对,该SN应属于网络开关");
                    }
                } else {
                    log.error("根据网络开关上线的SN,后台通过SN找不到网络开关设备");
                }*/
//                logger.info("该设备上线后的SessionManager===={}", SessionManager.getSessions());
                // 根据SN  查找网络设备  并上线   ps: SN=0090C2CA20EB
            } else {
                //不用特殊处理   直接正常应答心跳即可
            }
            //应答设备推送的心跳       减少打印
//            logger.info("应答心跳数据,=={}", receMsg.substring(1, receMsg.length() - 1).replace("CM", ""));
            ctx.writeAndFlush(hexStringToBytes(receMsg.substring(1, receMsg.length() - 1).replace("CM", "")));
        }
        if (receMsg.contains("$")) {
            ctx.writeAndFlush(("#"));
        }
        //处理DTU注册，注册包中一般不会带有业务数据  注册包处理  本次对接硬件没有注册包
        if (receMsg.toLowerCase().contains("a6") && receMsg.toLowerCase().contains("aa") && receMsg.length() > 15) {
            online(ctx, receMsg);
            return;
        }
        //处理跟硬件交互的业务数据
        receMsg = receMsg.replaceAll("\\[", "").replaceAll("\\{", "").replaceAll("\\}", "]").replaceAll("a60005ffb0", "");
        if (receMsg.length() > 0 && receMsg.contains("]")) {
            List<String> theMessages = Arrays.asList(receMsg.split("]"));
            if (theMessages.size() >= 1) {
                //减少日志打印
//                logger.info("-----------receive msg is : " + msg);
                for (String aMessage : theMessages) {
                    if (aMessage.contains("HS") || aMessage.contains("WD") || aMessage.contains("CM")) {
                        String gprsNo = "";
//                        if (aMessage.contains("CM50f6") || aMessage.contains("CM5343")|| aMessage.contains("CM594a")) {
                        if (aMessage.contains("CM50f6") || aMessage.contains("CM5343")) {
//                            log.info("业务aMessage=={}", aMessage);
// a50036CM50f6  303039304332434132304542  303039304332434132304542  dccd 01 aa e0  b1  0018   e2010000000001  e3010000000000 cddce5
                            // 设备应答数据   非心跳数据
                            gprsNo = aMessage.substring(12, 36);
                            // 十六进制SN-->10进制-->ASCII
                            String deviceNo = HexAsciiUtil.hex2Str(gprsNo);   //16进制设备SN转字符串
//                            logger.info("hexSn gprsNo==={}", gprsNo);
                            MDC.put("deviceIp", "控制器" + deviceNo);
                            logger.info("strSn deviceNo==={}", deviceNo);
                            //下面代码待调整
                            //命令字===   String  mycmd = aMessage.substring(6,12);
//                            aMessage = aMessage.substring(8);
                        } else {
                            gprsNo = SessionManager.getGprsNo(ctx);
                        }
                        //减少日志
//                        logger.info("aMessage==={}", aMessage);
                        String cmd = aMessage.substring(6, 12);
                    /* log.info("cmd=={}", cmd);*/
                        ChargeCmd command = ChargeCmd.valueOf(cmd);
                        switch (command) {
                            case CM594a:
                                wd20(ctx, gprsNo, aMessage);
                                break;
                            case CM50f6:
                                wd21(ctx, gprsNo, aMessage);
                                break;
                            case CM5343:
                                wd22(ctx, gprsNo, aMessage);
                                break;
                            default:
                                logger.info("Unhandled command: " + command);
                                break;
                        }
                    }
                }
            }
        }
    }

    //心跳命令
    private void wd20(ChannelHandlerContext ctx, String gprsNo, String aMessage) {
        Map<String, Object> params = new HashMap<>();
        //WD20123062
        try {
            params.put("gprsNo", gprsNo);
            params.put("state", "1");
            params.put("version", aMessage.substring(4, 9));
            if (aMessage.length() == 30) {
                params.put("iccid", aMessage.substring(9, 29));
            } else {
                params.put("iccid", "1");
            }
            params.put("cmd", "BS99");
//            SessionManager.putSession(gprsNo, ctx);
            //   sendMqMsgService.sendMsg(BaseConfig.CLT_EXCHANGE, BaseConfig.CLT_ROUTINGKEY, params);
        } catch (Exception e) {
            logger.info("------------handle CM20 error------------" + e.getMessage());
//            e.printStackTrace();
//            System.out.println("打印异常 MyBatisConfig=="+e.getMessage());
            log.error("打印异常  DataServerInHandler=={}", e.getMessage());
        }
    }

    /**
     * 控制输出 硬件返回被控制后的状态
     *
     * @param ctx
     * @param gprsNo
     * @param aMessage
     */
    private void wd21(ChannelHandlerContext ctx, String gprsNo, String aMessage) {
        String deviceNo = HexAsciiUtil.hex2Str(gprsNo);   //16进制设备SN转字符串
        log.info("sn={},该网关有开关量变化,aMessage={}", deviceNo, aMessage);


    }

    /**
     * 硬件开关量输入
     *
     * @param ctx
     * @param gprsNo
     * @param aMessage
     */
    private void wd22(ChannelHandlerContext ctx, String gprsNo, String aMessage) {

    }

    public static String getStringFromHex(String hex) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hex.length(); i += 2) {
            sb.append((char) Integer.parseInt(hex.substring(i, i + 2), 16));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        //System.out.println(hexStringToAlgorism("A113"));
//        byte[] bytes = hexStringToBytes("A5 00 2F 50 F6  38 30 33 32 35 33 46 30 33 31 36 46  31 31 31 31 31 31 31 31 31 31 31 31 DC CD 01 AA AA A1 00 11 E1 01 55 00 02 00 02 CD DC".replaceAll(" ",""));
//        byte[] bytes = hexStringToBytes("A5003d50F64143464443454446364135314A4451303034303030303032DCCD01AAAAA1001fE1012200020002E1025500020002E1035500020002CDDC".replaceAll(" ",""));
        byte[] bytes = hexStringToBytes("A5002F50F6303039304332434132304542313131313131313131313131DCCD01AAAAA10011E1015500000000CDDC".replaceAll(" ", ""));
        int check = 0;
        for (byte aByte : bytes) {
            check += Integer.parseInt(toHex(aByte), 16);
        }
        String checkCode = StringUtils.leftPad(Integer.toHexString(check), 2, "0");
        if (checkCode.length() >= 3) {
            checkCode = checkCode.substring(checkCode.length() - 2);
        }
        System.out.println(checkCode);
            System.err.println(hexStringToAlgorism("0002"));

        //ctx.writeAndFlush(hexStringToBytes("A60007" + msgNo + "EE01" + checkCode));
    }

    /**
     * 获取校验码    前面所有字节相加和
     *
     * @param allData
     * @return
     */
    public static String getCheckCodeByAllData(String allData) {
        byte[] bytes = hexStringToBytes(allData.replaceAll(" ", ""));
        int check = 0;
        for (byte aByte : bytes) {
            check += Integer.parseInt(toHex(aByte), 16);
        }
        String checkCode = StringUtils.leftPad(Integer.toHexString(check), 2, "0");
        if (checkCode.length() >= 3) {
            checkCode = checkCode.substring(checkCode.length() - 2);
        }
        return checkCode;
    }

    /**
     * 待写
     * 获取有效数据长度 (针对 本次硬件对接  北京科星的网络开关 )
     *
     * @param s
     * @return
     */

    public static String getValidDataHexLength(String s) {
        if (s.length() % 2 != 0) {
            s = "0" + s;
        }
        Integer dataLength = s.length() / 2;
        return NumericalUtil.BaseConvert(String.valueOf(dataLength), 10, 16);
    }


    private void online(ChannelHandlerContext ctx, String msg) throws IOException, TimeoutException {
        //a6003330aa 38363837333530343834323630333700 383938363030323531323833303630313233343500 0000000000000000 d3
        String msgNo = msg.substring(6, 8);
        String imei = getStringFromHex(msg.substring(10, 40));
        String iccid = getStringFromHex(msg.substring(42, 82));
        String version = getStringFromHex(msg.substring(84, 96));
        Map<String, Object> params = new HashMap<>();
        params.put("gprsNo", imei);
        params.put("iccid", iccid);
        params.put("version", version);
        params.put("msgNo", msgNo);
        params.put("cmd", "BS99");
//        sendMqMsgService.sendMsg(BaseConfig.CLT_EXCHANGE, BaseConfig.CLT_ROUTINGKEY, params);
//        SessionManager.putSession(imei, ctx);

        //给硬件回复消息
        /*byte[] bytes = hexStringToBytes("A60007" + msgNo + "EE01");
        int check = 0;
        for (byte aByte : bytes) {
            check += Integer.parseInt(toHex(aByte), 16);
        }
        String checkCode = StringUtils.leftPad(Integer.toHexString(check), 2, "0");
        if (checkCode.length() >= 3) {
            checkCode = checkCode.substring(checkCode.length() - 2);
        }
        ctx.writeAndFlush(hexStringToBytes("A60007" + msgNo + "EE01" + checkCode));*/
    }


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //ctx.writeAndFlush("121212");
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelWritabilityChanged(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel incoming = ctx.channel();
        if (!incoming.isActive()) {
            System.out.println("SimpleClient:" + incoming.remoteAddress() + "??");
        }
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            //将evt向下转型IdleStateEvent
            IdleStateEvent event = (IdleStateEvent) evt;
            String eventType = null;
            switch (event.state()) {
                case READER_IDLE:
                    eventType = "读空闲";
                    break;
                case WRITER_IDLE:
                    eventType = "写空闲";
                    break;
                case ALL_IDLE:
                    eventType = "读写空闲";
                    break;
                default:
                    break;
            }
//            System.out.println(ctx.channel().remoteAddress() + "---超时事件---" + eventType);
            log.info("{},---超时事件---,{}", ctx.channel().remoteAddress(), eventType);
            //如果发生空闲，关闭通道
            ctx.channel().close();
        }
    }

    public static byte[] hexStringToBytes(String hexString) {
        hexString = hexString.replaceAll(" ", ""); // 去空格
        if ((hexString == null) || (hexString.equals(""))) {
            return null;
        }
        hexString = hexString.toUpperCase(); // 字符串中的所有字母都被转化为大写字母
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; ++i) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[(pos + 1)]));
        }
        return d;
    }

    /**
     * char转字节数
     *
     * @param c char
     * @return 字节数
     */
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    /**
     * 把字节转化成字符串
     *
     * @param b 要转化的字节
     * @return 转化后的字符串
     */
    public static String toHex(byte b) {
        String result = Integer.toHexString(b & 0xFF);
        if (result.length() == 1) {
            result = '0' + result;
        }
        return result;
    }

    /**
     * 获取CRC校验码
     *
     * @param data
     * @return
     */
    public static String Make_CRC(byte[] data) {
        byte[] buf = new byte[data.length];// 存储需要产生校验码的数据
        for (int i = 0; i < data.length; i++) {
            buf[i] = data[i];
        }
        int len = buf.length;
        int crc = 0xFFFF;
        for (int pos = 0; pos < len; pos++) {
            if (buf[pos] < 0) {
                crc ^= (int) buf[pos] + 256; // XOR byte into least sig. byte of
                // crc
            } else {
                crc ^= (int) buf[pos]; // XOR byte into least sig. byte of crc
            }
            for (int i = 8; i != 0; i--) { // Loop over each bit
                if ((crc & 0x0001) != 0) { // If the LSB is set
                    crc >>= 1; // Shift right and XOR 0xA001
                    crc ^= 0xA001;
                } else {
                    // Else LSB is not set
                    crc >>= 1;
                } // Just shift right
            }
        }
        String c = Integer.toHexString(crc);
        if (c.length() == 4) {
            c = c.substring(2, 4) + c.substring(0, 2);
        } else if (c.length() == 3) {
            c = "0" + c;
            c = c.substring(2, 4) + c.substring(0, 2);
        } else if (c.length() == 2) {
            c = "0" + c.substring(1, 2) + "0" + c.substring(0, 1);
        }
        return c;
    }

    /**
     * 十六进制字符串转为10进制
     *
     * @param hex
     * @return
     */
    public static String getDeviceIdByHex(String hex) {
        String s = String.valueOf(Long.parseLong(hex, 16));
        if (s.equals("0")) {
            s = "0";
        }
        return s;
    }

    /**
     * 十六进制字符串转为10进制zi
     *
     * @param hex
     * @return
     */
    public static String getShiByHex(String hex) {
        String s = String.valueOf(Long.parseLong(hex, 16));
        if (s.equals("0")) {
            s = "0";
        } else {
            s = s.substring(0, s.length() - 1) + "." + s.substring(s.length() - 1);
        }
        return s;
    }

    public static String getShiByHex2(String hex) {
        String s = String.valueOf(Long.parseLong(hex, 16));
        if (s.equals("0")) {
            s = "0";
        } else {
            s = s.substring(0, s.length() - 2) + "." + s.substring(s.length() - 2);
        }
        return s;
    }

    /**
     * 十六进制字符串装十进制
     *
     * @param hex 十六进制字符串
     * @return 十进制数值
     */
    public static int hexStringToAlgorism(String hex) {
        hex = hex.toUpperCase();
        int max = hex.length();
        int result = 0;
        for (int i = max; i > 0; i--) {
            char c = hex.charAt(i - 1);
            int algorism = 0;
            if (c >= '0' && c <= '9') {
                algorism = c - '0';
            } else {
                algorism = c - 55;
            }
            result += Math.pow(16, max - i) * algorism;
        }
        return result;
    }

    /**
     * ASCII码字符串转数字字符串
     *
     * @param content ASCII字符串
     * @return 字符串
     */
    public static String AsciiStringToString(String content) {
        String result = "";
        int length = content.length() / 2;
        for (int i = 0; i < length; i++) {
            String c = content.substring(i * 2, i * 2 + 2);
            int a = hexStringToAlgorism(c);
            char b = (char) a;
            String d = String.valueOf(b);
            result += d;
        }
        return result;
    }

}
